use super::Assets;
use crate::services::user_service::UserService;
use actix_session::UserSession;
use actix_web::FromRequest;
use chrono::{DateTime as CDateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::convert::TryFrom;
use std::future::ready;
use std::future::Ready;
use std::ops::BitOr;
use std::str::FromStr;
use wither::bson::oid::ObjectId;
use wither::bson::Bson;
use wither::bson::DateTime;
use wither::Model;
mod serde_default {
    use super::*;
    pub fn user_role_role() -> u8 {
        UserRole::Student as u8
    }
    pub fn user_gender() -> UserGender {
        UserGender::Unknown
    }
}

#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum UserGender {
    Unknown = 0,
    Male = 1,
    Female = 2,
}

impl From<UserGender> for Bson {
    fn from(g: UserGender) -> Self {
        Bson::Int32(g as i32)
    }
}

#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum UserRole {
    Guest = 0,
    Student = 1 << 0,
    Leader = 1 << 1,
    Admin = 1 << 2,
    LeaderOrAdmin = (1 << 1) | (1 << 2),
    SuperAdmin = 1 << 3,
}
impl_into_bson!(UserRole);

impl UserRole {
    pub fn contain_in_actors(&self, actors: &[UserActor]) -> bool {
        let u8_self = *self as u8;
        actors
            .iter()
            .map(|v| v.role)
            .any(|v| v & u8_self == u8_self)
    }
    pub fn from_usize(val: usize) -> Vec<UserRole> {
        let mut res = vec![];
        if val & (UserRole::Student as usize) != 0 {
            res.push(UserRole::Student);
        }
        if val & (UserRole::Leader as usize) != 0 {
            res.push(UserRole::Leader);
        }
        if val & (UserRole::Admin as usize) != 0 {
            res.push(UserRole::Admin);
        }
        if val & (UserRole::SuperAdmin as usize) != 0 {
            res.push(UserRole::SuperAdmin);
        }
        res
    }
}

impl BitOr for UserRole {
    type Output = u8;

    fn bitor(self, rhs: Self) -> Self::Output {
        <UserRole as Into<u8>>::into(self) | <UserRole as Into<u8>>::into(rhs)
    }
}
impl From<UserRole> for u8 {
    fn from(u: UserRole) -> Self {
        u as u8
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UserActor {
    pub club_id: Option<ObjectId>,
    /// 文章板块id
    pub article_section_id: Option<ObjectId>,
    #[serde(default = "serde_default::user_role_role")]
    pub role: u8,
}
impl_into_bson!(UserActor);

/// ## UserActor对应的DTO
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserActorDTO {
    pub club_id: Option<String>,
    pub article_section_id: Option<String>,
    pub role: u8,
}

impl From<UserActor> for UserActorDTO {
    fn from(u: UserActor) -> Self {
        Self {
            club_id: u.club_id.as_ref().map(ToString::to_string),
            article_section_id: u.article_section_id.as_ref().map(ToString::to_string),
            role: u.role,
        }
    }
}
impl TryFrom<UserActorDTO> for UserActor {
    type Error = wither::bson::oid::Error;
    fn try_from(value: UserActorDTO) -> Result<Self, Self::Error> {
        let club_id = {
            match value.club_id.as_ref().map(|v| ObjectId::from_str(v)) {
                Some(v) => Some(v?),
                None => None,
            }
        };
        let article_section_id = {
            match value
                .article_section_id
                .as_ref()
                .map(|v| ObjectId::from_str(v))
            {
                Some(v) => Some(v?),
                None => None,
            }
        };

        Ok(Self {
            club_id,
            article_section_id,
            role: value.role,
        })
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserProfile {
    pub theme: Option<String>,
    pub extra: serde_json::Value,
}

impl_into_bson!(UserProfile);

impl Default for UserProfile {
    fn default() -> Self {
        Self {
            theme: None,
            extra: serde_json::json!({}),
        }
    }
}
#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct UserModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub nickname: String,
    pub name: Option<String>,
    pub comment: Option<String>,
    pub email: Option<String>,
    pub qq: Option<String>,
    pub wechat: Option<String>,
    #[serde(default = "serde_default::user_gender")]
    pub gender: UserGender,
    pub avatar: Option<Assets>,
    #[serde(default = "Vec::new")]
    pub actors: Vec<UserActor>,
    pub profile: Option<UserProfile>,
    pub birthday: Option<DateTime>,
    pub up_time: Option<DateTime>,
    pub created_at: DateTime,
    pub updated_at: DateTime,
}
impl_modifyed_for_model!(UserModel, updated_at);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NormalUserModel {
    pub id: ObjectId,
    pub nickname: String,
    pub avatar: Option<Assets>,
    pub actors: Vec<UserActor>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimpleUser {
    pub id: String,
    pub nickname: String,
    pub avatar: Option<Assets>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimpleUserModel {
    pub id: ObjectId,
    pub nickname: String,
    pub avatar: Option<Assets>,
}
impl_into_bson!(SimpleUserModel);

impl Default for SimpleUserModel {
    fn default() -> Self {
        Self {
            id: ObjectId::new(),
            nickname: String::new(),
            avatar: None,
        }
    }
}

impl From<UserModel> for SimpleUser {
    fn from(model: UserModel) -> Self {
        Self {
            id: model.id.map(|o| o.to_string()).unwrap_or_else(|| "".into()),
            nickname: model.nickname,
            avatar: model.avatar,
        }
    }
}
impl From<NormalUserModel> for SimpleUser {
    fn from(model: NormalUserModel) -> Self {
        let user: UserModel = model.into();
        user.into()
    }
}

impl From<SimpleUserModel> for SimpleUser {
    fn from(model: SimpleUserModel) -> Self {
        Self {
            id: model.id.to_string(),
            nickname: model.nickname,
            avatar: model.avatar,
        }
    }
}
impl From<UserModel> for SimpleUserModel {
    fn from(model: UserModel) -> Self {
        Self {
            id: model.id.unwrap_or_default(),
            avatar: model.avatar,
            nickname: model.nickname,
        }
    }
}
impl From<UserModel> for NormalUserModel {
    fn from(model: UserModel) -> Self {
        Self {
            id: model.id.unwrap_or_default(),
            avatar: model.avatar,
            nickname: model.nickname,
            actors: model.actors,
        }
    }
}
impl From<NormalUserModel> for SimpleUserModel {
    fn from(model: NormalUserModel) -> Self {
        Self {
            id: model.id,
            avatar: model.avatar,
            nickname: model.nickname,
        }
    }
}

impl Default for UserModel {
    fn default() -> Self {
        Self {
            id: None,
            nickname: "".to_string(),
            name: None,
            comment: None,
            email: None,
            qq: None,
            wechat: None,
            gender: UserGender::Unknown,
            avatar: None,
            actors: Vec::new(),
            birthday: None,
            up_time: None,
            profile: None,
            created_at: Utc::now().into(),
            updated_at: Utc::now().into(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserModelDTO {
    pub id: Option<String>,
    pub nickname: String,
    pub avatar: Option<Assets>,
    pub name: Option<String>,
    pub comment: Option<String>,
    pub email: Option<String>,
    pub qq: Option<String>,
    pub wechat: Option<String>,
    pub gender: UserGender,
    pub profile: Option<UserProfile>,
    pub created_at: CDateTime<Utc>,
}

impl From<UserModel> for UserModelDTO {
    fn from(u: UserModel) -> Self {
        Self {
            id: u.id.map(|i| i.to_string()),
            nickname: u.nickname,
            avatar: u.avatar,
            name: u.name,
            comment: u.comment,
            email: u.email,
            qq: u.qq,
            wechat: u.wechat,
            gender: u.gender,
            profile: u.profile,
            created_at: u.created_at.into(),
        }
    }
}

impl From<NormalUserModel> for UserModel {
    fn from(model: NormalUserModel) -> Self {
        Self {
            id: Some(model.id),
            nickname: model.nickname,
            actors: model.actors,
            ..Default::default()
        }
    }
}

impl FromRequest for UserModel {
    type Error = actix_web::Error;

    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(
        req: &actix_web::HttpRequest,
        _payload: &mut actix_http::Payload,
    ) -> Self::Future {
        let session = req.get_session();

        let user = UserService::get_user_from_session(&session).map_err(actix_web::Error::from);

        ready(user)
    }
}

impl FromRequest for NormalUserModel {
    type Error = actix_web::Error;

    type Future = Ready<Result<Self, Self::Error>>;
    fn from_request(
        req: &actix_web::HttpRequest,
        _payload: &mut actix_http::Payload,
    ) -> Self::Future {
        let session = req.get_session();
        let user = UserService::get_user_from_session(&session)
            .map(NormalUserModel::try_from)
            .map_err(actix_web::Error::from);
        match user {
            Ok(o) => ready(o.map_err(actix_web::Error::from)),
            Err(e) => ready(Err(e)),
        }
    }
}
